﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cosmos.IL2CPU.Plugs;
using Assembler = Cosmos.Compiler.Assembler.Assembler;
using CPUx86 = Cosmos.Assembler.x86;
using System.Runtime.InteropServices;

namespace Optimus
{
   unsafe class Manager
   {
        //System Call Data
        //Func(arg0,arg1)
        //Func(arg0) etc
        public static uint* SystemCall = (uint*)(0x50C);//Function Number
        public static uint* SystemArg0 = (uint*)(0x510);//Argument 0
        public static uint* SystemArg1 = (uint*)(0x514);//Argument 1
        public static uint* SystemArg2 = (uint*)(0x518);//Argument 2
        public static uint* SystemArg3 = (uint*)(0x51C);//Argument 3
        public static uint* SystemArg4 = (uint*)(0x520);//Argument 4 
        public static uint* SystemArg5 = (uint*)(0x524);//Argument 5
        public static byte[] CodeToRunx = new byte[11] { 0xB8, 0x02, 0x4F, 0xBB, 0x05, 0x01, 0xCD, 0x10, 0xE9, 0xFD, 0xFF };

        public static byte[] CodeToRun0 = new byte[5] { 0xE9, 0xFB, 0xFF, 0xFF, 0xFF };
        public static byte[] CodeToRun1 = new byte[30] { 0xE9, 0x00, 0x00, 0x00, 0x00, 0x66, 0xA3, 0x00, 0x00, 0x80, 0x00, 0xCD, 0x81, 0x66, 0x40, 0x66, 0x3D, 0x04, 0x00, 0x75, 0xF0, 0x66, 0xB8, 0x00, 0x00, 0xE9, 0xE7, 0xFF, 0xFF, 0xFF };
        public static byte[] CodeToRun2 = new byte[30] { 0xE9, 0x00, 0x00, 0x00, 0x00, 0x66, 0xA3, 0x00, 0x00, 0x80, 0x00, 0xCD, 0x80, 0x66, 0x40, 0x66, 0x3D, 0x04, 0x00, 0x75, 0xF0, 0x66, 0xB8, 0x00, 0x00, 0xE9, 0xE7, 0xFF, 0xFF, 0xFF };
      
        public static void HandleIRQ0D(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.CursorLeft = 0;
            Console.CursorTop = 0;
            Console.Write("Fault: ");
            uint d = xHAL.ErrorInfo();

            if ((d & 0x4) == 0x4)
            {
                Console.Write("IDT");
            }
            else
            {
                Console.Write("GDT");
            }
            while (true) { } //For Now Halt*/ //Ignore
        }

        public static void Memcpy(byte* OldPointer, byte* NewPointer, int Length)
      {
          int y=0;
          for (int i = 0; i < Length; i++)
          {
              *(byte*)(NewPointer + i) = (byte)(*(byte*)(OldPointer + i));
          }

      }

        public static void Memcpy(byte* NewPointer, byte[] Data, int Length)
       {
           for (int i = 0; i < Length; i++)
           {
               *(byte*)(NewPointer + i) = Data[i];
           }
       }
 
        public static void Init()
       {

        //   ATAPI.Init();
       //    ATA.Init();
           Cosmos.Core.INTs.SetIntHandler(0x0D, HandleIRQ0D);
           Cosmos.Core.INTs.SetIntHandler(0x81, HandleIRQX);
           Cosmos.Core.INTs.SetIntHandler(0x80, HandleIRQS);
           Cosmos.Core.INTs.SetIrqHandler(0x0D, HandleIRQD);
           Cosmos.Core.INTs.SetIrqHandler(0x0C, HandleIRQC);
           Cosmos.Core.INTs.SetIrqHandler(0x0B, HandleIRQB);
           Cosmos.Core.INTs.SetIrqHandler(0x0A, HandleIRQA);
           Cosmos.Core.INTs.SetIrqHandler(0x09, HandleIRQ9);
           RTC.Init();//IRQ8
           Cosmos.Core.INTs.SetIrqHandler(0x07, HandleIRQ7);
       //    FDD.Init(true);//IRQ6
           Cosmos.Core.INTs.SetIrqHandler(0x05, HandleIRQ5);
           Cosmos.Core.INTs.SetIrqHandler(0x04, HandleIRQ4);
           Cosmos.Core.INTs.SetIrqHandler(0x03, HandleIRQ3);
           Cosmos.Core.INTs.SetIrqHandler(0x02, HandleIRQ2);
           // Keyboard is IRQ1
           Cosmos.Core.INTs.SetIrqHandler(0x00, HandleIRQ0);
       }

        static int x4 = 0;
        static int y4 = 0;
        static uint tmp,tmp0,tmp1,tmp2,tmp3,tmp4;
        public static uint func = 0;
        public static void HandleIRQS(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            //func = (uint)*(uint*)(SystemCall);
     /*       if (func == 0x1)
            {
                VGA.SetMode(VGA.ScreenResolution.Res320x200x256);
                VGA.Clear(0);
            }
            else if (func == 0x2)
            {
                VGA.SetMode(VGA.ScreenResolution.Res320x240x256);
                VGA.Clear(0);
            }
            else if (func == 0x3)
            {
                VGA.SetMode(VGA.ScreenResolution.Res320x400x8);
                VGA.Clear(0);
            }
            else if (func == 0x4)
            {
                VGA.SetMode(VGA.ScreenResolution.Res320x480x8);
                VGA.Clear(0);
            }
            else if (func == 0x5)
            {
                tmp =  (uint)*(uint*)(SystemArg0);
                tmp0 = (uint)*(uint*)(SystemArg1);
                tmp1 = (uint)*(uint*)(SystemArg2);
                tmp2 = (uint)*(uint*)(SystemArg3);
                tmp3 = (uint)*(uint*)(SystemArg4);
                tmp4 = (uint)*(uint*)(SystemArg5);
                NovaDraw2D.DrawFillRectangle(tmp, tmp0, tmp1, tmp2, (byte)tmp3);
            }
            else if (func == 0x6)
            {
                tmp = (uint)*(uint*)(SystemArg0);
                tmp0 = (uint)*(uint*)(SystemArg1);
                tmp1 = (uint)*(uint*)(SystemArg2);
                tmp2 = (uint)*(uint*)(SystemArg3);
                tmp3 = (uint)*(uint*)(SystemArg4);
                tmp4 = (uint)*(uint*)(SystemArg5);
                NovaDraw2D.DrawRectangle(tmp, tmp0, tmp1, tmp2, (byte)tmp3);
            }
            else if (func == 0x7)
            {
                tmp = (uint)*(uint*)(SystemArg0);
                tmp0 = (uint)*(uint*)(SystemArg1);
                tmp1 = (uint)*(uint*)(SystemArg2);
                tmp2 = (uint)*(uint*)(SystemArg3);
                tmp3 = (uint)*(uint*)(SystemArg4);
                tmp4 = (uint)*(uint*)(SystemArg5);
                NovaDraw2D.DrawFillSquare(tmp, tmp0, tmp1, (byte)tmp3);
            }
            else if (func == 0x8)
            {
                tmp = (uint)*(uint*)(SystemArg0);
                tmp0 = (uint)*(uint*)(SystemArg1);
                tmp1 = (uint)*(uint*)(SystemArg2);
                tmp2 = (uint)*(uint*)(SystemArg3);
                tmp3 = (uint)*(uint*)(SystemArg4);
                tmp4 = (uint)*(uint*)(SystemArg5);
                NovaDraw2D.DrawSquare(tmp, tmp0, tmp1, (byte)tmp3);
            }*/
            Console.CursorLeft = 0;
            Console.CursorTop = 2;
            Console.Write("ThreadA: " + x4.ToString());

            x4++; 
        }

        public static void IdleTask()
        {
            while (true)
            {
                Console.CursorLeft = 0;
                Console.CursorTop = 3;
                Console.Write("Test");
            }
        }

        public static void HandleIRQX(ref Cosmos.Core.INTs.IRQContext aContext)
        {
          Console.CursorLeft = 0;
          Console.CursorTop = 3;
          Console.Write("ThreadB: " + y4.ToString());
          y4++;
        }

        public static void HandleIRQ2(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQ2");
        }

        public static void HandleIRQ3(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQ3");
        }

        public static void HandleIRQ4(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQ4");
        }

        public static void HandleIRQ5(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQ5");
        }

        public static void HandleIRQ6(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("************************* Cpu Error *************************");
            Console.WriteLine("------------------------Invalid Opcode!-------------------------");
            while (true) { } //For Now Halt
        }

        public static void HandleIRQ7(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQ7");
        }

        public static void HandleIRQ9(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQ9");
        }

        public static void HandleIRQA(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQA");
        }

        public static void HandleIRQB(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQB");
        }

        public static void HandleIRQC(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQC");
        }

        public static void HandleIRQD(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQD");
        }

        public static void HandleIRQE(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            Console.WriteLine("IRQE");
        }

        public static void HandleIRQF(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            
            Console.WriteLine("IRQF");
        }


        public static int tick = 5;
        public static uint ESPY = 0;
        public static void HandleIRQ0(ref Cosmos.Core.INTs.IRQContext aContext)
        {
            if (tick == 5)
            {
                tick = 0;// 5 * 18 times per second
                xHAL.InitialiseMultitasking();
            }
            tick++;
        }
       
        public unsafe struct Thread
        {        
            public uint* ESP0;   //Stack for process
            public uint ESPx; 
            public uint Segement;
            public uint EIP;
            public uint HighMem;
            public uint LowMem;
        }

     public static Thread[] Threads = new Thread[256]; //256 Tasks
     public static int CurrentTask = -1;
     public static int TotalTasks = -1;

     public static unsafe void CreateTask(int id, byte[] Data)
        {
          //Works 100%
            uint Size = 0;
            Size = (uint)Data.Length + 60;
           
            uint StackFrame = (uint)Cosmos.Core.Heap.MemAlloc(Size + 4096);//Pointer to Stack of program
            Threads[id] = new Thread();

            Threads[id].ESP0 = (uint*)(StackFrame + 4096);//Stack is 60 bytes long

            Threads[id].LowMem = StackFrame;
            Threads[id].HighMem = StackFrame + Size + 4096;
            Memcpy((byte*)(StackFrame + 4097), Data, Data.Length);//Copy program directly after stack

            //First, this stuff is pushed by the processor
            *--Threads[id].ESP0 = 0x0202; //This is EFLAGS
            *--Threads[id].ESP0 = 0x08;   //This is CS, our code segment
            *--Threads[id].ESP0 = (uint)(StackFrame + 4097); //This is EIP

            //Next, the stuff pushed by 'pusha'
            *--Threads[id].ESP0 = 0; //EDI
            *--Threads[id].ESP0 = 0; //ESI
            *--Threads[id].ESP0 = 0; //EBP
            *--Threads[id].ESP0 = 0; //Just an offset, no value
            *--Threads[id].ESP0 = 0; //EBX
            *--Threads[id].ESP0 = 0; //EDX
            *--Threads[id].ESP0 = 0; //ECX
            *--Threads[id].ESP0 = 0; //EAX

            //Now these are the data segments pushed by the IRQ handler
            *--Threads[id].ESP0 = 0x10; //DS
            *--Threads[id].ESP0 = 0x10; //ES
            *--Threads[id].ESP0 = 0x10; //FS
            *--Threads[id].ESP0 = 0x10; //GS*/

            Threads[id].ESPx = (uint)Threads[id].ESP0;

            Threads[id].Segement = 0x10;
            Threads[id].EIP = (uint)(StackFrame + 4097);
             TotalTasks++;
         
        }
     public static unsafe void CreateUserTask(int id, byte[] Data)
     {
         //Works 100%
         uint Size = 0;
         Size = (uint)Data.Length + 60; 

        
         uint StackFrame = (uint)Cosmos.Core.Heap.MemAlloc(Size + 4096);//Pointer to Stack of program

         Threads[id].ESP0 = (uint*)(StackFrame + 4096);//Stack is 60 bytes long
         Memcpy((byte*)(StackFrame + 4097), Data, Data.Length);//Copy program directly after stack
         //First, this stuff is pushed by the processor
         *--Threads[id].ESP0 = 0x23; //This is EFLAGS
         *--Threads[id].ESP0 = 0;   //This is CS, our code segment
         *--Threads[id].ESP0 = 0x0200; //This is EFLAGS
         *--Threads[id].ESP0 = 0x1b;   //This is CS, our code segment
         *--Threads[id].ESP0 = (uint)(StackFrame + 4097); //This is EIP


         //Next, the stuff pushed by 'pusha'
         *--Threads[id].ESP0 = 0; //EDI
         *--Threads[id].ESP0 = 0; //ESI
         *--Threads[id].ESP0 = 0; //EBP
         *--Threads[id].ESP0 = 0; //Just an offset, no value
         *--Threads[id].ESP0 = 0; //EBX
         *--Threads[id].ESP0 = 0; //EDX
         *--Threads[id].ESP0 = 0; //ECX
         *--Threads[id].ESP0 = 0; //EAX

         //Now these are the data segments pushed by the IRQ handler
         *--Threads[id].ESP0 = 0x23; //DS
         *--Threads[id].ESP0 = 0x23; //ES
         *--Threads[id].ESP0 = 0x23; //FS
         *--Threads[id].ESP0 = 0x23; //GS*/

         Threads[id].ESPx = (uint)Threads[id].ESP0;

         Threads[id].Segement = 0x23;

         Threads[id].EIP = (uint)(StackFrame + 4097);
         TotalTasks++;
     }
    
     public static Core.MemoryMonitor Kx = new Core.MemoryMonitor();
     
     public static uint scheduler(uint OldEsp)  // Our assembly code provides us with the context
        {
            if (CurrentTask == -1)
            {
               Threads[CurrentTask].ESP0 = (uint*)OldEsp;                
                CurrentTask++;
            }
            else if (CurrentTask != -1 )
            {
                ESPY = OldEsp;
                Cosmos.Core.INTs.System_TSS.ESP0 = OldEsp;
                Threads[CurrentTask].ESP0 = (uint*)(Threads[CurrentTask].ESPx);//Save the new esp for the thread
                Threads[CurrentTask].ESP0[14] = 0x0202;
                Threads[CurrentTask].ESP0[13] = 0x08;
                Threads[CurrentTask].ESP0[8] = OldEsp;
                Threads[CurrentTask].ESP0[3] = Threads[CurrentTask].Segement;
                Threads[CurrentTask].ESP0[2] = Threads[CurrentTask].Segement;
                Threads[CurrentTask].ESP0[1] = Threads[CurrentTask].Segement;
                Threads[CurrentTask].ESP0[0] = Threads[CurrentTask].Segement;
                CurrentTask++;
                if (CurrentTask > TotalTasks )
                {
                    CurrentTask = 0;
                }
            }
         
            return Threads[CurrentTask].ESPx; //Return new stack pointer to ASM*/
        }
 
   }
}
